
#include <material/io/iodefines.h>
#include <material/io/materialwriter.h>
#include <material/materialmanager.h>
#include <session.h>

#include <qdom.h>
#include <qfile.h>

cceMaterialWriter::cceMaterialWriter(cceMaterialManager* manager)
  : _manager (manager)
{
}

void cceMaterialWriter::WriteAll()
{
  WriteTextures ();
  WriteShaders ();
  WritePrograms ();
  WriteEffects ();
  WriteMaterials ();
}


/* *********************************
 *   Writing textures
 * *********************************/

void cceMaterialWriter::WriteTextures()
{
  QDomDocument doc ("textures");
  WriteTextures(doc);

  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_TEXTURES_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::WriteOnly))
    {
      printf ("Unable to open file: '%s'\n", (const char*)filename.toAscii());
      fflush(stdout);
      return;
    }

  file.write(doc.toString(4).toAscii());
  file.close();
}

void cceMaterialWriter::WriteTextures(QDomDocument &doc)
{
  QDomElement texturesElement = doc.createElement(CCE_MATERIAL_IO_XML_TEXTURES);

  for (unsigned i=0, numTextures=_manager->GetNumberOfTextures(); i<numTextures; ++i)
    {
      WriteTexture (doc, texturesElement, _manager->GetTexture(i));
    }
  doc.appendChild(texturesElement);
}

void cceMaterialWriter::WriteTexture(QDomDocument &doc, QDomElement &parent, cceMaterialTexture* texture)
{
  QDomElement textureElement = doc.createElement(CCE_MATERIAL_IO_XML_TEXTURE);
  textureElement.setAttribute(CCE_MATERIAL_IO_XML_TEXTURE_NAME, texture->GetName());

  QDomElement filenameElement = doc.createElement(CCE_MATERIAL_IO_XML_TEXTURE_FILENAME);
  filenameElement.appendChild(doc.createTextNode(texture->GetFilename()));
  textureElement.appendChild(filenameElement);

  QDomElement groupElement = doc.createElement(CCE_MATERIAL_IO_XML_TEXTURE_GROUP);
  groupElement.appendChild(doc.createTextNode(texture->GetGroup()));
  textureElement.appendChild(groupElement);

  QDomElement wrapElement = doc.createElement(CCE_MATERIAL_IO_XML_TEXTURE_WRAP);
  wrapElement.appendChild(doc.createTextNode(cceMaterialTexture::GetWrapName(texture->GetWrap())));
  textureElement.appendChild(wrapElement);

  parent.appendChild(textureElement);
}

/* *********************************
 *   Writing textures
 * *********************************/

void cceMaterialWriter::WriteShaders()
{
  QDomDocument doc ("shaders");
  WriteShaders(doc);

  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_SHADERS_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::WriteOnly))
    {
      printf ("Unable to open file: '%s'\n", (const char*)filename.toAscii());
      fflush(stdout);
      return;
    }

  file.write(doc.toString(4).toAscii());
  file.close();
}

void cceMaterialWriter::WriteShaders(QDomDocument &doc)
{
	QDomElement shadersElement = doc.createElement(CCE_MATERIAL_IO_XML_SHADERS);

  for (unsigned i=0, numShaders=_manager->GetNumberOfShaders(); i<numShaders; ++i)
    {
      WriteShader(doc, shadersElement, _manager->GetShader(i));
    }
  doc.appendChild(shadersElement);
}

void cceMaterialWriter::WriteShader(QDomDocument &doc, QDomElement &parent, cceMaterialShader *shader)
{
	QDomElement shaderElement = doc.createElement(CCE_MATERIAL_IO_XML_SHADER);
	shaderElement.setAttribute(CCE_MATERIAL_IO_XML_SHADER_NAME, shader->GetName());
	shaderElement.setAttribute(CCE_MATERIAL_IO_XML_SHADER_TYPE, cceMaterialShader::GetTypeName(shader->GetType()));

  shaderElement.appendChild(doc.createCDATASection(shader->GetCode()));

  parent.appendChild(shaderElement);
}


/* *********************************
 *   Writing programs
 * *********************************/

void cceMaterialWriter::WritePrograms()
{
  QDomDocument doc ("programs");
  WritePrograms(doc);

  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_PROGRAMS_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::WriteOnly))
    {
      printf ("Unable to open file: '%s'\n", (const char*)filename.toAscii());
      fflush(stdout);
      return;
    }

  file.write(doc.toString(4).toAscii());
  file.close();
}

void cceMaterialWriter::WritePrograms(QDomDocument &doc)
{
	QDomElement programsElement = doc.createElement(CCE_MATERIAL_IO_XML_PROGRAMS);

  for (unsigned i=0, numPrograms =_manager->GetNumberOfPrograms(); i<numPrograms; ++i)
    {
      WriteProgram (doc, programsElement, _manager->GetProgram(i));
    }
  doc.appendChild(programsElement);
}

void cceMaterialWriter::WriteProgram (QDomDocument &doc, QDomElement &parent, cceMaterialProgram *program)
{
	QDomElement programElement = doc.createElement(CCE_MATERIAL_IO_XML_PROGRAM);
	programElement.setAttribute(CCE_MATERIAL_IO_XML_PROGRAM_NAME, program->GetName());


	QDomElement techniquesElement = doc.createElement(CCE_MATERIAL_IO_XML_PROGRAM_TECHNIQUES);

  for (unsigned i=0, numTechniques=program->GetNumberOfTechniques(); i<numTechniques; ++i)
    {
      cceMaterialProgramTechnique *technique = program->GetTechnique(i);
      if (!technique)
        {
          continue;
        }
			QDomElement techniqueElement = doc.createElement(CCE_MATERIAL_IO_XML_PROGRAM_TECHNIQUE);

      for (unsigned j=0, numShaders=technique->GetNumberOfShaders(); j<numShaders; ++j)
        {
          cceMaterialShader* shader = technique->GetShader(j);
          if (!shader)
            {
              continue;
            }
					QDomElement shaderElement = doc.createElement(CCE_MATERIAL_IO_XML_PROGRAM_SHADER);
          shaderElement.appendChild(doc.createTextNode(shader->GetName()));
          techniqueElement.appendChild(shaderElement);
        }


      techniquesElement.appendChild(techniqueElement);
    }

  programElement.appendChild(techniquesElement);
  parent.appendChild(programElement);
}


/* *********************************
 *   Writing effects
 * *********************************/

void cceMaterialWriter::WriteEffects()
{
	QDomDocument doc ("effects");
  WriteEffects(doc);

  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_EFFECTS_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::WriteOnly))
    {
      printf ("Unable to open file: '%s'\n", (const char*)filename.toAscii());
      fflush(stdout);
      return;
    }

  file.write(doc.toString(4).toAscii());
  file.close();
}

void cceMaterialWriter::WriteEffects(QDomDocument &doc)
{
	QDomElement effectsElement = doc.createElement(CCE_MATERIAL_IO_XML_EFFECTS);

  for (unsigned i=0, numEffects =_manager->GetNumberOfEffects(); i<numEffects; ++i)
    {
      WriteEffect(doc, effectsElement, _manager->GetEffect(i));
    }
  doc.appendChild(effectsElement);
}

void cceMaterialWriter::WriteEffect(QDomDocument &doc, QDomElement &parent, cceMaterialEffect *effect)
{
	QDomElement effectElement = doc.createElement(CCE_MATERIAL_IO_XML_EFFECT);
	effectElement.setAttribute(CCE_MATERIAL_IO_XML_EFFECT_NAME, effect->GetName());


	QDomElement stagesElement = doc.createElement(CCE_MATERIAL_IO_XML_EFFECT_STAGES);

  for (unsigned i=0, numStages=effect->GetNumberOfStages(); i<numStages; ++i)
    {
      cceMaterialEffectStage *stage = effect->GetStage(i);
      if (!stage)
        {
          continue;
        }
			QDomElement stageElement = doc.createElement(CCE_MATERIAL_IO_XML_EFFECT_STAGE);

      for (unsigned j=0; j<Pass_COUNT; ++j)
        {
          cceMaterialProgram* program = stage->GetPass((cceMaterialPass)j);
          if (!program)
            {
              continue;
            }

					QDomElement passElement = doc.createElement(CCE_MATERIAL_IO_XML_EFFECT_PASS);
					passElement.setAttribute(CCE_MATERIAL_IO_XML_EFFECT_NAME, cceMaterialEffectStage::GetPassName((cceMaterialPass)j));
          passElement.appendChild(doc.createTextNode(program->GetName()));

          stageElement.appendChild(passElement);
        }


      stagesElement.appendChild(stageElement);
    }

  effectElement.appendChild(stagesElement);
  parent.appendChild(effectElement);
}


/* *********************************
 *   Writing materials
 * *********************************/

void cceMaterialWriter::WriteMaterials()
{
  QDomDocument doc ("materials");
  WriteMaterials(doc);

  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_MATERIALS_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::WriteOnly))
    {
      printf ("Unable to open file: '%s'\n", (const char*)filename.toAscii());
      fflush(stdout);
      return;
    }

  file.write(doc.toString(4).toAscii());
  file.close();
}

void cceMaterialWriter::WriteMaterials(QDomDocument &doc)
{
	QDomElement materialsElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIALS);

  for (unsigned i=0, numMaterials=_manager->GetNumberOfMaterials(); i<numMaterials; ++i)
    {
      WriteMaterial(doc, materialsElement, _manager->GetMaterial(i));
    }
  doc.appendChild(materialsElement);
}

namespace
{
void append_color (QDomDocument &doc, QDomElement &parent, const QString &name, const QColor& color)
{
  QDomElement element = doc.createElement(name);
  element.setAttribute("r", color.red());
  element.setAttribute("g", color.green());
  element.setAttribute("b", color.blue());
  element.setAttribute("a", color.alpha());
  parent.appendChild(element);
}
}

void cceMaterialWriter::WriteMaterial(QDomDocument &doc, QDomElement &parent, cceMaterialMaterial *material)
{
	QDomElement materialElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL);
	materialElement.setAttribute(CCE_MATERIAL_IO_XML_MATERIAL_NAME, material->GetName());

	append_color (doc, materialElement, CCE_MATERIAL_IO_XML_MATERIAL_AMBIENT, material->GetAmbient());
	append_color (doc, materialElement, CCE_MATERIAL_IO_XML_MATERIAL_DIFFUSE, material->GetDiffuse());
	append_color (doc, materialElement, CCE_MATERIAL_IO_XML_MATERIAL_SPECULAR, material->GetSpecular());
	QDomElement shininessElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL_SHININESS);
	shininessElement.setAttribute(CCE_MATERIAL_IO_XML_MATERIAL_VALUE, material->GetShininess());
  materialElement.appendChild(shininessElement);
	append_color (doc, materialElement, CCE_MATERIAL_IO_XML_MATERIAL_EMISSION, material->GetEmission());

	QDomElement effectElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL_EFFECT);
  cceMaterialEffect* effect = material->GetEffect();
  if (effect)
    {
      effectElement.appendChild(doc.createTextNode(effect->GetName()));
    }
  materialElement.appendChild(effectElement);

	QDomElement texturesElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL_TEXTURES);
  for (unsigned i=0; i<Pass_COUNT; ++i)
    {
      cceMaterialPass pass = (cceMaterialPass)i;
      cceMaterialTexturePack* pack = material->GetTextures(pass);
      if (!pack)
        {
          continue;
        }

			QDomElement passElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL_PASS);
			passElement.setAttribute(CCE_MATERIAL_IO_XML_MATERIAL_NAME, cceMaterialEffectStage::GetPassName(pass));
      for (unsigned j=0; j<16; ++j)
        {
          cceMaterialTexture* texture = pack->GetTexture(j);
          if (!texture)
            {
              continue;
            }
					QDomElement textureElement = doc.createElement(CCE_MATERIAL_IO_XML_MATERIAL_TEXTURE);
					textureElement.setAttribute(CCE_MATERIAL_IO_XML_MATERIAL_UNIT, j);
          textureElement.appendChild(doc.createTextNode(texture->GetName()));
          passElement.appendChild(textureElement);
        }

      texturesElement.appendChild(passElement);
    }


  materialElement.appendChild(texturesElement);

  parent.appendChild(materialElement);
}


